package com.innovation.ic.b1b.monitor.web.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.google.common.collect.Lists;
import com.innovation.ic.b1b.monitor.base.pojo.constant.config.DatabaseGlobal;
import com.innovation.ic.b1b.monitor.base.value.*;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.apache.shardingsphere.api.config.masterslave.LoadBalanceStrategyConfiguration;
import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithm;
import org.apache.shardingsphere.shardingjdbc.api.MasterSlaveDataSourceFactory;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

@EnableTransactionManagement
@Configuration
@MapperScan({"com.innovation.ic.b1b.monitor.base.mapper", "com.innovation.ic.im.end.base.mapper"})
public class MybatisPlusConfig {
    @Resource
    private ShardingB1bMonitor0DataSourceConfig shardingB1bMonitor0DataSourceConfig;

    @Resource
    private ShardingB1bMonitor1DataSourceConfig shardingB1bMonitor1DataSourceConfig;

    @Resource
    private ShardingB1bMonitor2DataSourceConfig shardingB1bMonitor2DataSourceConfig;

    @Resource
    private ImDataSourceConfig imDataSourceConfig;

    @Resource
    private ShardingConfig shardingConfig;

    /**
     * 批量插入
     *
     * @return
     */
    @Bean
    public EasySqlInjector easySqlInjector() {
        return new EasySqlInjector();
    }

    /**
     * 分页
     *
     * @return
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * sql拦截器
     *
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//        mybatisPlusInterceptor.addInnerInterceptor(new DistributedTransactionInterceptor());
        return mybatisPlusInterceptor;
    }

    @Bean(name = DatabaseGlobal.B1B_MONITOR0)
    public DataSource b1bMonitor0() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(shardingB1bMonitor0DataSourceConfig.getDriverClassName());//如果不配置druid会根据url自动识别dbType，然后选择相应的driverClassName
        dataSource.setUrl(shardingB1bMonitor0DataSourceConfig.getUrl());
        dataSource.setUsername(shardingB1bMonitor0DataSourceConfig.getUsername());
        dataSource.setPassword(shardingB1bMonitor0DataSourceConfig.getPassword());
        dataSource.setMaxActive(shardingB1bMonitor0DataSourceConfig.getMaxActive());
        dataSource.setInitialSize(shardingB1bMonitor0DataSourceConfig.getInitialSize());
        dataSource.setMinIdle(shardingB1bMonitor0DataSourceConfig.getMinIdle());
        return dataSource;
    }

    @Bean(name = DatabaseGlobal.B1B_MONITOR1)
    public DataSource b1bMonitor1() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(shardingB1bMonitor1DataSourceConfig.getDriverClassName());//如果不配置druid会根据url自动识别dbType，然后选择相应的driverClassName
        dataSource.setUrl(shardingB1bMonitor1DataSourceConfig.getUrl());
        dataSource.setUsername(shardingB1bMonitor1DataSourceConfig.getUsername());
        dataSource.setPassword(shardingB1bMonitor1DataSourceConfig.getPassword());
        dataSource.setMaxActive(shardingB1bMonitor1DataSourceConfig.getMaxActive());
        dataSource.setInitialSize(shardingB1bMonitor1DataSourceConfig.getInitialSize());
        dataSource.setMinIdle(shardingB1bMonitor1DataSourceConfig.getMinIdle());
        return dataSource;
    }

    @Bean(name = DatabaseGlobal.B1B_MONITOR2)
    public DataSource b1bMonitor2() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(shardingB1bMonitor2DataSourceConfig.getDriverClassName());//如果不配置druid会根据url自动识别dbType，然后选择相应的driverClassName
        dataSource.setUrl(shardingB1bMonitor2DataSourceConfig.getUrl());
        dataSource.setUsername(shardingB1bMonitor2DataSourceConfig.getUsername());
        dataSource.setPassword(shardingB1bMonitor2DataSourceConfig.getPassword());
        dataSource.setMaxActive(shardingB1bMonitor2DataSourceConfig.getMaxActive());
        dataSource.setInitialSize(shardingB1bMonitor2DataSourceConfig.getInitialSize());
        dataSource.setMinIdle(shardingB1bMonitor2DataSourceConfig.getMinIdle());
        return dataSource;
    }

    @Bean(name = DatabaseGlobal.IM)
    public DataSource im() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(imDataSourceConfig.getDriverClassName());//如果不配置druid会根据url自动识别dbType，然后选择相应的driverClassName
        dataSource.setUrl(imDataSourceConfig.getUrl());
        dataSource.setUsername(imDataSourceConfig.getUsername());
        dataSource.setPassword(imDataSourceConfig.getPassword());
        dataSource.setMaxActive(imDataSourceConfig.getMaxActive());
        dataSource.setInitialSize(imDataSourceConfig.getInitialSize());
        dataSource.setMinIdle(imDataSourceConfig.getMinIdle());
        return dataSource;
    }

    /**
     * 动态数据源配置（分库分表）
     *
     * @return
     */
    @Bean(name = DatabaseGlobal.B1B_MONITOR_SHARDING)
    public DataSource shardingDataSource(@Qualifier(DatabaseGlobal.B1B_MONITOR0) DataSource b1bMonitor0,
                                         @Qualifier(DatabaseGlobal.B1B_MONITOR1) DataSource b1bMonitor1,
                                         @Qualifier(DatabaseGlobal.B1B_MONITOR2) DataSource b1bMonitor2) {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put(DatabaseGlobal.B1B_MONITOR1, b1bMonitor1);
        dataSourceMap.put(DatabaseGlobal.B1B_MONITOR2, b1bMonitor2);
        dataSourceMap.put(DatabaseGlobal.B1B_MONITOR0, b1bMonitor0);

        ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();

        // 需要分库分表的表
        shardingRuleConfiguration.getTableRuleConfigs().add(interfaceCallLogRuleConfig());
        shardingRuleConfiguration.getTableRuleConfigs().add(pageVisitLogRuleConfig());
        shardingRuleConfiguration.getTableRuleConfigs().add(userLoginLogRuleConfig());
        shardingRuleConfiguration.getTableRuleConfigs().add(userBehaviorLogRuleConfig());
        shardingRuleConfiguration.getTableRuleConfigs().add(frontEndLogRuleConfig());

        // 配置绑定表（未分片的表需要在此配置）（不起作用）
//        shardingRuleConfig.setBindingTableGroups(Arrays.asList("interface_call_log", "page_visit_log", "user_login_log"));
//        shardingRuleConfiguration.getBindingTableGroups().add("interface_call_log, page_visit_log, user_login_log");

        // 不需要分库分表的表
//        List<TableRuleConfiguration> list = noNeedRuleConfig();
//        if(list.size() > 0){
//            for(TableRuleConfiguration tableRuleConfiguration : list){
//                shardingRuleConfig.getTableRuleConfigs().add(tableRuleConfiguration);
//            }
//        }

        // 默认的分库策略（不起作用）
//        shardingRuleConfiguration.setDefaultDatabaseShardingStrategyConfig(new NoneShardingStrategyConfiguration());
        // 默认的分表策略（不起作用）
//        shardingRuleConfiguration.setDefaultTableShardingStrategyConfig(new NoneShardingStrategyConfiguration());

        // 对于没有分库分表的表，数据存储到这个数据源（不起作用）
        shardingRuleConfiguration.setDefaultDataSourceName(DatabaseGlobal.B1B_MONITOR2);

        Properties properties = new Properties();
        properties.setProperty("sql.show", shardingConfig.getSqlShow().toString());
        DataSource dataSource = null;
        try {
            dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, properties);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    /**
     * 动态数据源配置（读写分离）
     *
     * @return
     */
    @Bean(name = DatabaseGlobal.B1B_MONITOR_MASTER_SLAVE)
    public DataSource masterSlaveDataSource(@Qualifier(DatabaseGlobal.B1B_MONITOR0) DataSource b1bMonitor0,
                                            @Qualifier(DatabaseGlobal.B1B_MONITOR1) DataSource b1bMonitor1,
                                            @Qualifier(DatabaseGlobal.B1B_MONITOR2) DataSource b1bMonitor2) {
        MasterSlaveRuleConfiguration masterSlaveRuleConfiguration = new MasterSlaveRuleConfiguration(DatabaseGlobal.B1B_MONITOR_MASTER_SLAVE,
                DatabaseGlobal.B1B_MONITOR2,
                Arrays.asList(DatabaseGlobal.B1B_MONITOR0, DatabaseGlobal.B1B_MONITOR1));

        // 设置从库数据源集合
        Map<String, DataSource> slaveDataSourceMap = new HashMap<>();
        slaveDataSourceMap.put(DatabaseGlobal.B1B_MONITOR1, b1bMonitor1);
        slaveDataSourceMap.put(DatabaseGlobal.B1B_MONITOR0, b1bMonitor0);
        slaveDataSourceMap.put(DatabaseGlobal.B1B_MONITOR2, b1bMonitor2);

        // 设置主库数据源集合
        Properties properties = new Properties();
        properties.setProperty("sql.show", shardingConfig.getSqlShow().toString());
        DataSource dataSource = null;
        try {
            dataSource = MasterSlaveDataSourceFactory.createDataSource(slaveDataSourceMap, masterSlaveRuleConfiguration, properties);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier(DatabaseGlobal.B1B_MONITOR_SHARDING) DataSource shardingDataSource,
                                         @Qualifier(DatabaseGlobal.B1B_MONITOR_MASTER_SLAVE) DataSource masterSlaveDataSource,
                                         @Qualifier(DatabaseGlobal.IM) DataSource imDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DatabaseGlobal.B1B_MONITOR_SHARDING, shardingDataSource);
        targetDataSources.put(DatabaseGlobal.B1B_MONITOR_MASTER_SLAVE, masterSlaveDataSource);
        targetDataSources.put(DatabaseGlobal.IM, imDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 程序默认数据源，这个要根据程序调用数据源频次，经常把常调用的数据源作为默认
        dynamicDataSource.setDefaultTargetDataSource(shardingDataSource(b1bMonitor0(), b1bMonitor1(), b1bMonitor2()));
        return dynamicDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(shardingDataSource(b1bMonitor0(), b1bMonitor1(), b1bMonitor2()),
                masterSlaveDataSource(b1bMonitor0(), b1bMonitor1(), b1bMonitor2()),
                im()));
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        sqlSessionFactory.setGlobalConfig(globalConfiguration());
        // 乐观锁插件
        //PerformanceInterceptor(),OptimisticLockerInterceptor()
        // 分页插件
        sqlSessionFactory.setPlugins(paginationInterceptor());
        // 拦截sql的插件
        sqlSessionFactory.setPlugins(mybatisPlusInterceptor());
        return sqlSessionFactory.getObject();
    }

    @Bean
    public GlobalConfig globalConfiguration() {
        GlobalConfig conf = new GlobalConfig();
        // 自定义的注入需要在这里进行配置
        conf.setSqlInjector(easySqlInjector());
        return conf;
    }

    /**
     * interface_call_log表的分库分表规则
     *
     * @return
     */
    private TableRuleConfiguration interfaceCallLogRuleConfig() {
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("interface_call_log",
                "b1b-monitor${0..2}.interface_call_log_${0..1}");
        tableRuleConfig.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        tableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new DatabaseShardingAlgorithm(shardingConfig)));
        tableRuleConfig.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new IdCommonShardingAlgorithm(shardingConfig)));
        return tableRuleConfig;
    }

    /**
     * page_visit_log表的分库分表规则
     *
     * @return
     */
    private TableRuleConfiguration pageVisitLogRuleConfig() {
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("page_visit_log",
                "b1b-monitor${0..2}.page_visit_log_${0..1}");
        tableRuleConfig.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        tableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new DatabaseShardingAlgorithm(shardingConfig)));
        tableRuleConfig.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new IdCommonShardingAlgorithm(shardingConfig)));
        return tableRuleConfig;
    }

    /**
     * user_login_log表的分库分表规则
     *
     * @return
     */
    private TableRuleConfiguration userLoginLogRuleConfig() {
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("user_login_log",
                "b1b-monitor${0..2}.user_login_log_${0..1}");
        tableRuleConfig.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        tableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new DatabaseShardingAlgorithm(shardingConfig)));
        tableRuleConfig.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new IdCommonShardingAlgorithm(shardingConfig)));
        return tableRuleConfig;
    }

    /**
     * user_behavior_log表的分库分表规则
     *
     * @return
     */
    private TableRuleConfiguration userBehaviorLogRuleConfig() {
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("user_behavior_log",
                "b1b-monitor${0..2}.user_behavior_log_${0..1}");
        tableRuleConfig.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        tableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new DatabaseShardingAlgorithm(shardingConfig)));
        tableRuleConfig.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new IdCommonShardingAlgorithm(shardingConfig)));
        return tableRuleConfig;
    }

    /**
     * front_end_log表的分库分表规则
     *
     * @return
     */
    private TableRuleConfiguration frontEndLogRuleConfig() {
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("front_end_log",
                "b1b-monitor${0..2}.front_end_log_${0..1}");
        tableRuleConfig.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        tableRuleConfig.setDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new DatabaseShardingAlgorithm(shardingConfig)));
        tableRuleConfig.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("id", new IdCommonShardingAlgorithm(shardingConfig)));
        return tableRuleConfig;
    }

    /**
     * 主键生成策略
     *
     * @return
     */
    private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "id");
        return result;
    }

}